-- C761007.A
--
--
--                             Grant of Unlimited Rights
--
--     Under contracts F33600-87-D-0337, F33600-84-D-0280, MDA903-79-C-0687,
--     F08630-91-C-0015, and DCA100-97-D-0025, the U.S. Government obtained 
--     unlimited rights in the software and documentation contained herein.
--     Unlimited rights are defined in DFAR 252.227-7013(a)(19).  By making 
--     this public release, the Government intends to confer upon all 
--     recipients unlimited rights  equal to those held by the Government.  
--     These rights include rights to use, duplicate, release or disclose the 
--     released technical data and computer software in whole or in part, in 
--     any manner and for any purpose whatsoever, and to have or permit others 
--     to do so.
--
--                                    DISCLAIMER
--
--     ALL MATERIALS OR INFORMATION HEREIN RELEASED, MADE AVAILABLE OR
--     DISCLOSED ARE AS IS.  THE GOVERNMENT MAKES NO EXPRESS OR IMPLIED 
--     WARRANTY AS TO ANY MATTER WHATSOEVER, INCLUDING THE CONDITIONS OF THE
--     SOFTWARE, DOCUMENTATION OR OTHER INFORMATION RELEASED, MADE AVAILABLE 
--     OR DISCLOSED, OR THE OWNERSHIP, MERCHANTABILITY, OR FITNESS FOR A
--     PARTICULAR PURPOSE OF SAID MATERIAL.
--*
--
-- OBJECTIVE:
--      Check that if a finalize procedure invoked by a transfer of control
--      due to selection of a terminate alternative attempts to propagate an
--      exception, the exception is ignored, but any other finalizations due
--      to be performed are performed.
--
--
-- TEST DESCRIPTION:
--      This test declares a nested controlled data type, and embeds an object
--      of that type within a protected type.  Objects of the protected type
--      are created and destroyed, and the actions of the embedded controlled
--      object are checked.  The container controlled type causes an exception
--      as the last part of it's finalization operation.
--
--      This test utilizes several tasks to accomplish the objective.  The
--      tasks contain delays to ensure that the expected order of processing
--      is indeed accomplished.
--
--      Subtest 1:
--        local task object runs to normal completion
--
--      Subtest 2:
--        local task aborts a nested task to cause finalization
--
--      Subtest 3: 
--        local task sleeps long enough to allow procedure started
--        asynchronously to go into infinite loop.  Procedure is then aborted
--        via ATC, causing finalization of objects.
--
--      Subtest 4:
--        local task object takes terminate alternative, causing finalization
--
--
-- CHANGE HISTORY:
--      06 JUN 95   SAIC    Initial version
--      05 APR 96   SAIC    Documentation changes
--      03 MAR 97   PWB.CTA Allowed two finalization orders for ATC test
--      02 DEC 97   EDS     Remove duplicate characters from check string.
--!

---------------------------------------------------------------- C761007_0

with Ada.Finalization;
package C761007_0 is

  type Internal is new Ada.Finalization.Controlled
    with record
      Effect : Character;
    end record;

  procedure Finalize( I: in out Internal );

  Side_Effect : String(1..80);  -- way bigger than needed
  Side_Effect_Finger : Natural := 0;

end C761007_0;
 
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

with TCTouch;
package body C761007_0 is

  procedure Finalize( I : in out Internal ) is
    Previous_Side_Effect : Boolean := False;
  begin
    -- look to see if this character has been finalized yet
    for SEI in 1..Side_Effect_Finger loop
      Previous_Side_Effect := Previous_Side_Effect
                              or Side_Effect(Side_Effect_Finger) = I.Effect;
    end loop;

    -- if not, then tack it on to the string, and touch the character
    if not Previous_Side_Effect then
      Side_Effect_Finger := Side_Effect_Finger +1;
      Side_Effect(Side_Effect_Finger) := I.Effect;
      TCTouch.Touch(I.Effect);
    end if;

  end Finalize;

end C761007_0;

---------------------------------------------------------------- C761007_1

with C761007_0;
with Ada.Finalization;
package C761007_1 is

  type Container is new Ada.Finalization.Controlled
    with record
      Effect   : Character;
      Content  : C761007_0.Internal;
    end record;

  procedure Finalize( C: in out Container );

  Side_Effect : String(1..80);  -- way bigger than needed
  Side_Effect_Finger : Natural := 0;

  This_Exception_Is_Supposed_To_Be_Ignored : exception;

end C761007_1;
 
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

with TCTouch;
package body C761007_1 is

  procedure Finalize( C: in out Container ) is
    Previous_Side_Effect : Boolean := False;
  begin
    -- look to see if this character has been finalized yet
    for SEI in 1..Side_Effect_Finger loop
      Previous_Side_Effect := Previous_Side_Effect
                              or Side_Effect(Side_Effect_Finger) = C.Effect;
    end loop;

    -- if not, then tack it on to the string, and touch the character
    if not Previous_Side_Effect then
      Side_Effect_Finger := Side_Effect_Finger +1;
      Side_Effect(Side_Effect_Finger) := C.Effect;
      TCTouch.Touch(C.Effect);
    end if;

    raise This_Exception_Is_Supposed_To_Be_Ignored;

  end Finalize;

end C761007_1;
 
---------------------------------------------------------------- C761007_2
with C761007_1;
package C761007_2 is

  protected type Prot_W_Fin_Obj is
    procedure Set_Effects( Container, Filling: Character );
  private
    The_Data_Under_Test : C761007_1.Container;
    -- finalization for this will occur when the Prot_W_Fin_Obj object
    --  "goes out of existence" for whatever reason.
  end Prot_W_Fin_Obj;

end C761007_2;
 
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

package body C761007_2 is

  protected body Prot_W_Fin_Obj is
    procedure Set_Effects( Container, Filling: Character ) is
    begin
      The_Data_Under_Test.Effect := Container;           -- A, etc.
      The_Data_Under_Test.Content.Effect := Filling;     -- B, etc.
    end Set_Effects;
  end Prot_W_Fin_Obj;

end C761007_2;

------------------------------------------------------------------ C761007

with Report;
with Impdef;
with TCTouch;
with C761007_0;
with C761007_1;
with C761007_2;
procedure C761007 is

  task type Subtests( Outer, Inner : Character) is
    entry Ready;
    entry Complete;
  end Subtests;

  task body Subtests is
    Local_Prot_W_Fin_Obj : C761007_2.Prot_W_Fin_Obj;
  begin
    Local_Prot_W_Fin_Obj.Set_Effects( Outer, Inner );

    accept Ready;

    select
      accept Complete;
    or terminate;       -- used in Subtest 4
    end select;
  exception
    -- the exception caused by the finalization of Local_Prot_W_Fin_Obj
    --  should never be visible to this scope.
    when others => Report.Failed("Exception in a Subtest object "
                                 & Outer & Inner);
  end Subtests;

  procedure Subtest_1 is
    -- check the case where "nothing special" happens.

    This_Subtest : Subtests( 'A', 'B' );
  begin

    This_Subtest.Ready;
    This_Subtest.Complete;

    while not This_Subtest'Terminated loop -- wait for finalization
      delay Impdef.Clear_Ready_Queue;
    end loop;

    -- in the finalization of This_Subtest, the controlled object embedded in
    -- the Prot_W_Fin_Obj will finalize.  An exception is raised in the
    -- container object, after "touching" it's tag character.
    -- The finalization of the contained controlled object must be performed.


    TCTouch.Validate( "AB", "Item embedded in task" );


  exception
    when others => Report.Failed("Undesirable exception in Subtest_1");

  end Subtest_1;

  procedure Subtest_2 is
    -- check for explicit abort

    task Subtest_Task is
      entry Complete;
    end Subtest_Task;

    task body Subtest_Task is

      task Nesting;
      task body Nesting is
        Deep_Nesting : Subtests( 'E', 'F' );
      begin
        if Report.Ident_Bool( True ) then
          -- controlled objects have been created in the elaboration of
          -- Deep_Nesting.  Deep_Nesting must call the Set_Effects operation
          -- in the Prot_W_Fin_Obj, and then hang waiting for the Complete
          -- entry call.
          Deep_Nesting.Ready;
          abort Deep_Nesting;
        else
          Report.Failed("Dead code in Nesting");
        end if;
      exception
        when others => Report.Failed("Exception in Subtest_Task.Nesting");
      end Nesting;

      Local_2 : C761007_2.Prot_W_Fin_Obj;

    begin
      -- Nesting has activated at this point, which implies the activation
      -- of Deep_Nesting as well.

      Local_2.Set_Effects( 'C', 'D' );

      -- wait for Nesting to terminate

      while not Nesting'Terminated loop
        delay Impdef.Clear_Ready_Queue;
      end loop;

      accept Complete;

    exception
      when others => Report.Failed("Exception in Subtest_Task");
    end Subtest_Task;

  begin

    -- wait for everything in Subtest_Task to happen
    Subtest_Task.Complete;

    while not Subtest_Task'Terminated loop -- wait for finalization
      delay Impdef.Clear_Ready_Queue;
    end loop;

    TCTouch.Validate( "EFCD", "Aborted nested task" );

  exception
    when others => Report.Failed("Undesirable exception in Subtest_2");
  end Subtest_2;

  procedure Subtest_3 is
    -- check abort caused by asynchronous transfer of control

    task Subtest_3_Task is
      entry Complete;
    end Subtest_3_Task;

    procedure Check_Atc_Operation is
      Check_Atc : C761007_2.Prot_W_Fin_Obj;
    begin

      Check_Atc.Set_Effects( 'G', 'H' );


      while Report.Ident_Bool( True ) loop -- wait to be aborted
        if Report.Ident_Bool( True ) then
          Impdef.Exceed_Time_Slice;
          delay Impdef.Switch_To_New_Task;
        else
          Report.Failed("Optimization prevention");
        end if;
      end loop;

      Report.Failed("Check_Atc_Operation loop completed");

    end Check_Atc_Operation;

    task body Subtest_3_Task is
      task Nesting is
        entry Complete;
      end Nesting;

      task body Nesting is
        Nesting_3 : C761007_2.Prot_W_Fin_Obj;
      begin
        Nesting_3.Set_Effects( 'G', 'H' );

        -- give Check_Atc_Operation sufficient time to perform it's
        -- Set_Effects on it's local Prot_W_Fin_Obj object
        delay Impdef.Clear_Ready_Queue;

        accept Complete;
      exception
        when others => Report.Failed("Exception in Subtest_3_Task.Nesting");
      end Nesting;

      Local_3 : C761007_2.Prot_W_Fin_Obj;

    begin -- Subtest_3_Task

      Local_3.Set_Effects( 'I', 'J' );

      select
        Nesting.Complete;
      then abort ---------------------------------------------------- cause KL
        Check_ATC_Operation;
      end select;

      accept Complete;

    exception
      when others => Report.Failed("Exception in Subtest_3_Task");
    end Subtest_3_Task;

  begin -- Subtest_3
    Subtest_3_Task.Complete;

    while not Subtest_3_Task'Terminated loop -- wait for finalization
      delay Impdef.Clear_Ready_Queue;
    end loop;

    TCTouch.Validate( "GHIJ", "Asynchronously aborted operation" );

  exception
    when others => Report.Failed("Undesirable exception in Subtest_3");
  end Subtest_3;

  procedure Subtest_4 is
    -- check the case where transfer is caused by terminate alternative
    -- highly similar to Subtest_1

    This_Subtest : Subtests( 'M', 'N' );
  begin

    This_Subtest.Ready;
    -- don't call This_Subtest.Complete;

  exception
    when others => Report.Failed("Undesirable exception in Subtest_4");

  end Subtest_4;

begin  -- Main test procedure.

  Report.Test ("C761007", "Check that if a finalize procedure invoked by " &
                          "a transfer of control or selection of a " &
                          "terminate alternative attempts to propagate " &
                          "an exception, the exception is ignored, but " &
                          "any other finalizations due to be performed " &
                          "are performed" );

  Subtest_1;  -- checks internal

  Subtest_2;  -- checks internal

  Subtest_3;  -- checks internal

  Subtest_4;
  TCTouch.Validate( "MN", "transfer due to terminate alternative" );

  Report.Result;

end C761007;
